/* Copyright Statement:
 *
 * This software/firmware and related documentation ("AutoChips Software") are
 * protected under relevant copyright laws. The information contained herein is
 * confidential and proprietary to AutoChips Inc. and/or its licensors. Without
 * the prior written permission of AutoChips inc. and/or its licensors, any
 * reproduction, modification, use or disclosure of AutoChips Software, and
 * information contained herein, in whole or in part, shall be strictly
 * prohibited.
 *
 * AutoChips Inc. (C) 2023. All rights reserved.
 *
 * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
 * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("AUTOCHIPS SOFTWARE")
 * RECEIVED FROM AUTOCHIPS AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
 * ON AN "AS-IS" BASIS ONLY. AUTOCHIPS EXPRESSLY DISCLAIMS ANY AND ALL
 * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NONINFRINGEMENT. NEITHER DOES AUTOCHIPS PROVIDE ANY WARRANTY WHATSOEVER WITH
 * RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
 * INCORPORATED IN, OR SUPPLIED WITH THE AUTOCHIPS SOFTWARE, AND RECEIVER AGREES
 * TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
 * RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
 * OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN AUTOCHIPS
 * SOFTWARE. AUTOCHIPS SHALL ALSO NOT BE RESPONSIBLE FOR ANY AUTOCHIPS SOFTWARE
 * RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
 * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND AUTOCHIPS'S
 * ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE AUTOCHIPS SOFTWARE
 * RELEASED HEREUNDER WILL BE, AT AUTOCHIPS'S OPTION, TO REVISE OR REPLACE THE
 * AUTOCHIPS SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
 * CHARGE PAID BY RECEIVER TO AUTOCHIPS FOR SUCH AUTOCHIPS SOFTWARE AT ISSUE.
 */

/*!
 * @file ac780x_adc.c
 *
 * @brief This file provides analog to digital converter module integration functions.
 *
 */

/* ===========================================  Includes  =========================================== */
#include "ac780x_adc_reg.h"
#include "ac780x_dma.h"
#include <math.h>

/* ============================================  Define  ============================================ */
#define ADC_AVGCOUNT       (16UL)
/* ===========================================  Typedef  ============================================ */

/* ==========================================  Variables  =========================================== */
/* ADC related info */
static const IRQn_Type s_adcIRQ[ADC_INSTANCE_MAX] = {ADC0_IRQn};

/* ADC callback pointer */
static DeviceCallback_Type s_adcCallback[ADC_INSTANCE_MAX] = {NULL};

/* ====================================  Functions declaration  ===================================== */

/* ======================================  Functions define  ======================================== */
/*!
 * @brief ADC initialize.
 *
 * @param[in] ADCx: adc module
 *               - ADC0
 * @param[in] config: pointer to configuration structure
 * @return none
 */
void ADC_Init(ADC_Type *ADCx, const ADC_ConfigType *config)
{
    uint8_t instance = 0U;

    instance = ADC_INDEX(ADCx);

    DEVICE_ASSERT(ADC_INSTANCE_MAX > instance);
    DEVICE_ASSERT(NULL != config);
    DEVICE_ASSERT(ADC_REGULAR_SEQUENCE_LEGNTH >= config->regularSequenceLength);
    DEVICE_ASSERT(ADC_INJECT_SEQUENCE_LEGNTH >= config->injectSequenceLength);

    /* Enbale adc clock */
    CKGEN_Enable(CLK_ADC0, ENABLE);
    CKGEN_SoftReset(SRST_ADC0, ENABLE);

    /* Set clock prescaler */
    ADC_SetClockPrescaler(ADCx, config->clkPsc);

    /* Set adc mode */
    ADC_SetScanMode(ADCx, config->scanModeEn);
    ADC_SetContinuousMode(ADCx, config->continuousModeEn);
    ADC_SetRegularDiscontinuousMode(ADCx, config->regularDiscontinuousModeEn);
    ADC_SetInjectDiscontinuousMode(ADCx, config->injectDiscontinuousModeEn);
    ADC_SetInjectAutoMode(ADCx, config->injectAutoModeEn);
    ADC_SetIntervalMode(ADCx, config->intervalModeEn);
    ADC_SetRegularDiscontinuousNum(ADCx, config->regularDiscontinuousNum);
    ADC_SetRegularTriggerSource(ADCx, config->regularTriggerMode);
    ADC_SetInjectTriggerSource(ADCx, config->injectTriggerMode);
    ADC_SetDataAlign(ADCx, config->dataAlign);

    /* Set ADC reference voltage source */
    ADC_SetVREFSource(ADCx, config->voltageRef);

    /* Set adc sequence length */
    if (0 == config->regularSequenceLength)
    {
        ADC_SetRegularLength(ADCx, 0);
    }
    else
    {
        ADC_SetRegularLength(ADCx, config->regularSequenceLength - 1);
    }

    if (0 == config->injectSequenceLength)
    {
        ADC_SetInjectLength(ADCx, 0);
    }
    else
    {
        ADC_SetInjectLength(ADCx, config->injectSequenceLength - 1);
    }

    /* Register callback function */
    s_adcCallback[instance] = config->callBack;

    /* Set adc interrupt */
    ADC_SetEOCInterrupt(ADCx, config->EOCInterruptEn);
    ADC_SetIEOCInterrupt(ADCx, config->IEOCInterruptEn);
    if (ENABLE == config->interruptEn)
    {
        NVIC_EnableIRQ(s_adcIRQ[instance]);
    }
    else
    {
        NVIC_DisableIRQ(s_adcIRQ[instance]);
    }

    /* Set adc dma */
    ADC_SetDMA(ADCx, config->regularDMAEn);

    /* Set power */
    ADC_SetPowerMode(ADCx, config->powerMode);
}

/*!
 * @brief ADC De-initialize.
 *
 * @param[in] ADCx: adc module
 *               - ADC0
 * @return none
 */
void ADC_DeInit(ADC_Type *ADCx)
{
    uint8_t instance = 0U;

    instance = ADC_INDEX(ADCx);
    DEVICE_ASSERT(ADC_INSTANCE_MAX > instance);

    NVIC_DisableIRQ(s_adcIRQ[instance]);
    NVIC_ClearPendingIRQ(s_adcIRQ[instance]);
    CKGEN_SoftReset(SRST_ADC0, DISABLE);
    CKGEN_Enable(CLK_ADC0, DISABLE);
}

/*!
 * @brief Get ADC configuration.
 *
 * @param[in] ADCx: adc module
 *               - ADC0
 * @param[out] config: pointer to ADC configuration structure
 */
void ADC_GetConfig(ADC_Type *ADCx,  ADC_ConfigType * const config)
{
    uint8_t instance = 0U;

    instance = ADC_INDEX(ADCx);

    DEVICE_ASSERT(ADC_INSTANCE_MAX > instance);
    DEVICE_ASSERT(NULL != config);

    /* Get clock prescaler */
    config->clkPsc = (ADC_ClkPrescalerType)ADC_GetClockPrescaler(ADCx);

    /* Get adc mode */
    config->scanModeEn = (ACTION_Type)ADC_GetScanMode(ADCx);
    config->continuousModeEn = (ACTION_Type)ADC_GetContinuousMode(ADCx);
    config->regularDiscontinuousModeEn = (ACTION_Type)ADC_GetRegularDiscontinuousMode(ADCx);
    config->injectDiscontinuousModeEn = (ACTION_Type)ADC_GetInjectDiscontinuousMode(ADCx);
    config->injectAutoModeEn = (ACTION_Type)ADC_GetInjectAutoMode(ADCx);
    config->intervalModeEn = (ACTION_Type)ADC_GetIntervalMode(ADCx);
    config->regularDiscontinuousNum = (uint8_t)ADC_GetRegularDiscontinuousNum(ADCx);

    config->regularTriggerMode = (ADC_TriggerConvertType)ADC_GetRegularTriggerSource(ADCx);
    config->injectTriggerMode = (ADC_TriggerConvertType)ADC_GetInjectTriggerSource(ADCx);
    config->dataAlign = (ADC_DataAlignType)ADC_GetDataAlign(ADCx);

    config->regularSequenceLength = (uint8_t)ADC_GetRegularLength(ADCx) + 1;
    config->injectSequenceLength = (uint8_t)ADC_GetInjectLength(ADCx) + 1;

    config->voltageRef = (ADC_VrefType)ADC_GetVREFSource(ADCx);

    /* Register callback function */
    config->callBack = s_adcCallback[instance];

    /* Get adc interrupt */
    config->EOCInterruptEn = (ACTION_Type)ADC_GetEOCInterrupt(ADCx);
    config->IEOCInterruptEn = (ACTION_Type)ADC_GetIEOCInterrupt(ADCx);

    config->interruptEn = (ACTION_Type)NVIC_GetEnableIRQ(s_adcIRQ[instance]);

    /* Get adc dma */
    config->regularDMAEn = (ACTION_Type)ADC_GetDMA(ADCx);

    /* Get power */
    config->powerMode = (ADC_PowerModeType)ADC_GetPowerMode(ADCx);
}

/*!
 * @brief Set adc regular group channel.
 *
 * @param[in] ADCx: adc module
 *               - ADC0
 * @param[in] channel: adc channel
 *               - ADC_CH_0
 *               - ADC_CH_1
 *               - ...
 *               - ADC_CH_18
 *               - ADC_CH_VIN
 *               - ADC_CH_CALI
 * @param[in] spt: sample time
 *               - ADC_SPT_CLK_8
 *               - ADC_SPT_CLK_10
 *               - ADC_SPT_CLK_15
 *               - ADC_SPT_CLK_33
 *               - ADC_SPT_CLK_64
 *               - ADC_SPT_CLK_140
 *               - ADC_SPT_CLK_215
 *               - ADC_SPT_CLK_6
 * @param[in] seq: regular group convert sequence
 *               - 0 ~ 20
 * @return none
 */
void ADC_SetRegularGroupChannel(ADC_Type *ADCx, ADC_ChannelType channel, ADC_SampleTimeType spt, uint8_t seq)
{
    DEVICE_ASSERT(ADC_INSTANCE_MAX > ADC_INDEX(ADCx));
    DEVICE_ASSERT(ADC_REGULAR_SEQUENCE_LEGNTH > channel);
    DEVICE_ASSERT(ADC_SPT_CLK_MAX > spt);
    DEVICE_ASSERT(ADC_CHANNEL_MAX > seq);

    /* Set adc channel sample time */
    ADC_SetChannelSampleTime(ADCx, channel, spt);

    /* Set regular convert sequence */
    ADC_SetRegularSequence(ADCx, channel, seq);

    /*set VIN channel state*/
    if (channel == ADC_CH_VIN)
    {
        ADC_SetInternalChannelState(ADCx, ENABLE);
    }
}

/*!
 * @brief Get adc regular group channel and sample time.
 *
 * @param[in] ADCx: adc module
 *               - ADC0
 * @param[in] seq: regular group convert sequence
 *               - 0 ~ 20
 * @param[out] channel: adc channel
 *               - ADC_CH_0
 *               - ADC_CH_1
 *               - ...
 *               - ADC_CH_18
 *               - ADC_CH_VIN
 *               - ADC_CH_CALI
 * @param[out] spt: sample time
 *               - ADC_SPT_CLK_8
 *               - ADC_SPT_CLK_10
 *               - ADC_SPT_CLK_15
 *               - ADC_SPT_CLK_33
 *               - ADC_SPT_CLK_64
 *               - ADC_SPT_CLK_140
 *               - ADC_SPT_CLK_215
 *               - ADC_SPT_CLK_6
 * @return none
 */
void ADC_GetRegularGroupChannel(ADC_Type *ADCx, ADC_ChannelType *channel, ADC_SampleTimeType *spt, uint8_t seq)
{
    DEVICE_ASSERT(ADC_INSTANCE_MAX > ADC_INDEX(ADCx));
    DEVICE_ASSERT(ADC_REGULAR_SEQUENCE_LEGNTH > seq);
    DEVICE_ASSERT(NULL != channel);
    DEVICE_ASSERT(NULL != spt);

    /* Get regular convert sequence */
    *channel = (ADC_ChannelType)ADC_GetRegularSequence(ADCx, seq);

    /* Get adc channel sample time */
    *spt = (ADC_SampleTimeType)ADC_GetChannelSampleTime(ADCx, *channel);
}

/*!
 * @brief Set adc injected group channel.
 *
 * @param[in] ADCx: adc module
 *               - ADC0
 * @param[in] channel: adc channel
 *               - ADC_CH_0
 *               - ADC_CH_1
 *               - ...
 *               - ADC_CH_18
 *               - ADC_CH_VIN
 *               - ADC_CH_CALI
 * @param[in] spt: sample time
 *               - ADC_SPT_CLK_8
 *               - ADC_SPT_CLK_10
 *               - ADC_SPT_CLK_15
 *               - ADC_SPT_CLK_33
 *               - ADC_SPT_CLK_64
 *               - ADC_SPT_CLK_140
 *               - ADC_SPT_CLK_215
 *               - ADC_SPT_CLK_6
 * @param[in] seq: injected group convert sequence
 *               - 0 ~ 3
 * @return none
 */
void ADC_SetInjectGroupChannel(ADC_Type *ADCx, ADC_ChannelType channel, ADC_SampleTimeType spt, uint8_t seq)
{
    DEVICE_ASSERT(ADC_INSTANCE_MAX > ADC_INDEX(ADCx));
    DEVICE_ASSERT(ADC_CH_MAX > channel);
    DEVICE_ASSERT(ADC_INJECT_SEQUENCE_LEGNTH > seq);
    DEVICE_ASSERT(ADC_SPT_CLK_MAX > spt);

    /* Set adc channel sample time */
    ADC_SetChannelSampleTime(ADCx, channel, spt);

    /* Set injected convert sequence */
    ADC_SetInjectSequence(ADCx, channel, seq);

    /*set VIN channel state*/
    if (channel == ADC_CH_VIN)
    {
        ADC_SetInternalChannelState(ADCx, ENABLE);
    }
}

/*!
 * @brief Get adc injected group channel and sample time.
 *
 * @param[in] ADCx: adc module
 *               - ADC0
 * @param[in] seq: injected group convert sequence
 *               - 0 ~ 3
 * @param[out] channel: adc channel
 *               - ADC_CH_0
 *               - ADC_CH_1
 *               - ...
 *               - ADC_CH_18
 *               - ADC_CH_VIN
 *               - ADC_CH_CALI
 * @param[out] spt: sample time
 *               - ADC_SPT_CLK_8
 *               - ADC_SPT_CLK_10
 *               - ADC_SPT_CLK_15
 *               - ADC_SPT_CLK_33
 *               - ADC_SPT_CLK_64
 *               - ADC_SPT_CLK_140
 *               - ADC_SPT_CLK_215
 *               - ADC_SPT_CLK_6
 * @return none
 */
void ADC_GetInjectGroupChannel(ADC_Type *ADCx, ADC_ChannelType *channel, ADC_SampleTimeType *spt, uint8_t seq)
{
    DEVICE_ASSERT(ADC_INSTANCE_MAX > ADC_INDEX(ADCx));
    DEVICE_ASSERT(ADC_INJECT_SEQUENCE_LEGNTH > seq);
    DEVICE_ASSERT(NULL != channel);
    DEVICE_ASSERT(NULL != spt);

    /* Get injected convert sequence */
    *channel = (ADC_ChannelType)ADC_GetInjectSequence(ADCx, seq);

    /* Get adc channel sample time */
    *spt = (ADC_SampleTimeType)ADC_GetChannelSampleTime(ADCx, *channel);
}

/*!
 * @brief Set adc analog monitor.
 *
 * @param[in] ADCx: adc module
 *               - ADC0
 * @param[in] config: pointer to configuration structure
 * @return none
 */
void ADC_SetAnalogMonitor(ADC_Type *ADCx, ADC_AMOConfigType *config)
{
    DEVICE_ASSERT(ADC_INSTANCE_MAX > ADC_INDEX(ADCx));
    DEVICE_ASSERT(NULL != config);

    /* Set analog monitor mode */
    ADC_SetAMOTriggerMode(ADCx, config->AMOTriggerMode);
    ADC_SetAMOInterrupt(ADCx, config->AMOInterruptEn);
    ADC_SetAMORegularMode(ADCx, config->AMORegularEn);
    ADC_SetAMOInjectMode(ADCx, config->AMOInjectEn);
    ADC_SetAMOSingleChannelMode(ADCx, config->AMOSingleModeEn);

    ADC_SetAMOSingleChannel(ADCx, config->AMOSingleChannel);
    ADC_SetAMOThreshold(ADCx, config->AMOHighThreshold, config->AMOLowThreshold);
    ADC_SetAMOOffset(ADCx, config->AMOHighOffset, config->AMOLowOffset);
}

/*!
 * @brief Get adc analog monitor configuration.
 *
 * @param[in] ADCx: adc module
 *               - ADC0
 * @param[out] config: pointer to configuration structure
 * @return none
 */
void ADC_GetAnalogMonitor(ADC_Type *ADCx, ADC_AMOConfigType *config)
{
    DEVICE_ASSERT(ADC_INSTANCE_MAX > ADC_INDEX(ADCx));
    DEVICE_ASSERT(NULL != config);

    /* Set analog monitor mode */
    config->AMOTriggerMode = (ADC_AMOTriggerModeType)ADC_GetAMOTriggerMode(ADCx);
    config->AMOInterruptEn = (ACTION_Type)ADC_GetAMOInterrupt(ADCx);
    config->AMORegularEn = (ACTION_Type)ADC_GetAMORegularMode(ADCx);
    config->AMOInjectEn = (ACTION_Type)ADC_GetAMOInjectMode(ADCx);
    config->AMOSingleModeEn = (ACTION_Type)ADC_GetAMOSingleChannelMode(ADCx);
    config->AMOSingleChannel = (ADC_ChannelType)ADC_GetAMOSingleChannel(ADCx);

    config->AMOHighThreshold = (uint16_t)ADC_GetAMOHThreshold(ADCx);
    config->AMOLowThreshold = (uint16_t)ADC_GetAMOLThreshold(ADCx);
    config->AMOHighOffset = (uint16_t)ADC_GetAMOHOffset(ADCx);
    config->AMOLowOffset = (uint16_t)ADC_GetAMOLOffset(ADCx);
}

/*!
 * @brief ADC initialization delay.
 *
 * @return none
 */
static uint32_t cnt = 0U;
static void ADC_InitDelay(void)
{
    const uint32_t count = 1000U;

    for (cnt = 0; cnt < count; cnt++)
    {
        __asm("nop");
    }
}

/*!
 * @brief Get average value of ADC convert result from assigned sequence.
 *
 * @param[in] uADC_Value: ADC code array
 * @param[in] num: average count
 * @return averaged ADC result
 */
static uint16_t ADC_Average(uint16_t *uADC_Value, uint8_t num)
{
    uint16_t max, min, sum;
    uint8_t j = 0U;

    max = uADC_Value[0];
    min = uADC_Value[0];
    sum = 0U;
    j = 0U;

    for (j = 0; j < num; j++)
    {
        sum += uADC_Value[j];
        if(uADC_Value[j] > max)
        {
            max = uADC_Value[j];
        }
        else if (uADC_Value[j] < min)
        {
            min = uADC_Value[j];
        }
    }

    sum = sum - max - min;
    return (sum / (num - 2));
}

/*!
 * @brief ADC self calibration function, canbe used after calibration setting is configured.
 *
 * @param[in] ADCx: adc module
 *               - ADC0
 * @param[out] gain: calculated gain error result
 * @param[out] offset: calculated offset error result
 * @return none
 */
void ADC_SelfCalibration(ADC_Type *ADCx, int16_t *gain, int16_t *offset)
{
    float y1 = 0, y2 = 0, x1 = 0, x2 = 0, ge = 0, oe = 0;
    const uint8_t avgCount = ADC_AVGCOUNT;
    uint16_t calitmp[ADC_AVGCOUNT] = {0U};

    /* Get calibration value */
    ADC_SetGEOECaliSignal(ADCx, ADC_CALI_SIGNAL_VREF2_8);
    x1 = (float)2 / 8;    /* x1 from GEOEVIN value */

    for(int j = 0; j < avgCount; j++)
    {
        calitmp[j] = 0;
        ADC_SoftwareStartRegularConvert(ADCx);
        while (!ADC_GetEOCFlag(ADCx));
        calitmp[j] = ADC_GetRegularData(ADCx);
    }
    y1 = ADC_Average(calitmp, avgCount);

    ADC_SetGEOECaliSignal(ADCx, ADC_CALI_SIGNAL_VREF6_8);
    x2 = (float)6 / 8;    /* x2 from GEOEVIN value */

    for(int j = 0; j < avgCount; j++)
    {
        calitmp[j] = 0;
        ADC_SoftwareStartRegularConvert(ADCx);
        while (!ADC_GetEOCFlag(ADCx));
        calitmp[j] = ADC_GetRegularData(ADCx);
    }
    y2 = ADC_Average(calitmp, avgCount);

    /* Caculate calibration GE OE value */
    ge = (4095 * (x2 - x1) / (y2 - y1) - 1) * 4096;
    oe = (y2 * x1 - y1 * x2) * 4096 / (y2 - y1);
    DEVICE_ASSERT(ge <= 2047 && ge >= -2048);
    DEVICE_ASSERT(oe <= 1023 && oe >= -1024);

    *gain = (int16_t)round((double)ge);
    *offset = (int16_t)round((double)oe);
}

/*!
 * @brief Auto calibration function for ADC.  This function should be called every time after
 * ADC reset or power on.
 *
 * @param[in] ADCx: adc module
 *               - ADC0
 * @return none
 */
void ADC_DRV_AutoCalibration(ADC_Type *ADCx)
{
    int16_t ge, oe;
    ADC_ConfigType config;
    ADC_ChannelType channel_config;
    ADC_SampleTimeType spt_config;
    ADC_ConfigType adc_config;
    ADC_GEOEType adc_geoe;
    ACTION_Type geoe_en;
    uint8_t seq = 0U;

    ADC_SetExternalGainOffset(ADCx, 0, 0);

    /* save convert config setting */
    ADC_GetConfig(ADCx, &config);
    ADC_GetRegularGroupChannel(ADCx, &channel_config, &spt_config, seq);
    adc_geoe = ADC_GetGEOECaliSignal(ADCx);
    geoe_en = ADC_GetGEOECali(ADCx);

    /* Config ADC analog register to generate ge oe calibration signal */
    ADC_SetGEOECali(ADCx, ENABLE);

    /* config ADC convertion mode to mode 1 */
    adc_config.clkPsc = ADC_CLK_PRESCALER_16;
    adc_config.scanModeEn = DISABLE;
    adc_config.continuousModeEn = DISABLE;
    adc_config.regularDiscontinuousModeEn = DISABLE;
    adc_config.injectDiscontinuousModeEn = DISABLE;
    adc_config.injectAutoModeEn = DISABLE;
    adc_config.intervalModeEn = DISABLE;
    adc_config.regularDiscontinuousNum = 0;
    adc_config.interruptEn = DISABLE;
    adc_config.regularTriggerMode = ADC_TRIGGER_INTERNAL;
    adc_config.injectTriggerMode = ADC_TRIGGER_INTERNAL;
    adc_config.regularSequenceLength = 1;
    adc_config.injectSequenceLength = 1;
    adc_config.dataAlign = ADC_DATA_ALIGN_RIGHT;
    adc_config.voltageRef = ADC_VOLTAGEREF_VDDA;
    adc_config.powerMode = ADC_POWER_ON;
    adc_config.EOCInterruptEn = DISABLE;
    adc_config.IEOCInterruptEn = DISABLE;
    adc_config.regularDMAEn = DISABLE;
    adc_config.callBack = NULL;
    ADC_Init(ADCx, &adc_config);

    /* Disable calibration caculation logic */
    ADC_SetCalibration(ADCx, DISABLE);

    /* Config convertion channel */
    ADC_SetRegularGroupChannel(ADCx, ADC_CH_CALI, ADC_SPT_CLK_140, seq);

    ADC_InitDelay();

    /* calibrate external channel */
    ADC_SelfCalibration(ADCx, &ge, &oe);

    /* using external calibration parameter for internal channel */
    ADC_SetInternalGainOffset(ADCx, ge, oe);
    ADC_SetExternalGainOffset(ADCx, ge, oe);

    /* Restore ADC ANA setting */
    ADC_SetGEOECali(ADCx, DISABLE);

    /* Restore ADC setting */
    ADC_Init(ADCx, &config);
    ADC_SetRegularGroupChannel(ADCx, channel_config, spt_config, seq);
    ADC_SetGEOECaliSignal(ADCx, adc_geoe);
    ADC_SetGEOECali(ADCx, geoe_en);

    /* Enable calibration caculation after GE OE is ready */
    ADC_SetCalibration(ADCx, ENABLE);
}

/*!
 * @brief Set adc receive dma.
 *
 * @param[in] ADCx: adc module
 *               - ADC0
 * @param[in] DMAx:DMA Channel type pointer,
 *                 -DMA0_CHANNEL0
 *                 -DMA0_CHANNEL1
 *                 -DMA0_CHANNEL2
 *                 -DMA0_CHANNEL3
 * @param[in] rxBuffer: point to receive buffer
 * @param[in] transferNum: Transfer data numbers, 0~32767, bit15 should be 0
 * @param[in] callback: point to DMA callback function
 * @return none
 */
void ADC_SetReceiveDMA(ADC_Type *ADCx, DMA_ChannelType *DMAx, uint16_t *rxBuffer,
                       uint16_t transferNum, DeviceCallback_Type callback)
{
    DMA_ConfigType dmaConfig;

    DEVICE_ASSERT(ADC_INSTANCE_MAX > ADC_INDEX(ADCx));
    DEVICE_ASSERT(IS_DMA_PERIPH(DMAx));
    DEVICE_ASSERT(IS_DMA_TRANSFER_NUM(transferNum));

    dmaConfig.memStartAddr = (uint32_t)rxBuffer;
    dmaConfig.memEndAddr = dmaConfig.memStartAddr + (transferNum * 2);
    dmaConfig.periphStartAddr = (uint32_t)(&(ADCx->RDR));
    dmaConfig.channelEn = ENABLE;
    if(callback == NULL)
    {
        dmaConfig.finishInterruptEn = DISABLE;
        dmaConfig.halfFinishInterruptEn = DISABLE;
        dmaConfig.errorInterruptEn = DISABLE;
    }
    else
    {
        dmaConfig.finishInterruptEn = ENABLE;
        dmaConfig.halfFinishInterruptEn = DISABLE;
        dmaConfig.errorInterruptEn = ENABLE;
    }
    dmaConfig.channelPriority = DMA_PRIORITY_VERY_HIGH;
    dmaConfig.circular = ENABLE;
    dmaConfig.direction = DMA_READ_FROM_PERIPH;
    dmaConfig.MEM2MEM = DISABLE;
    dmaConfig.memByteMode = DMA_MEM_BYTE_MODE_1TIME;
    dmaConfig.memIncrement = ENABLE;
    dmaConfig.periphIncrement = DISABLE;
    dmaConfig.memSize = DMA_MEM_SIZE_16BIT;
    dmaConfig.periphSize = DMA_PERIPH_SIZE_16BIT;
    dmaConfig.periphSelect = DMA_PEPIRH_ADC0;
    dmaConfig.transferNum = transferNum;
    dmaConfig.callBack = callback;
    DMA_Init(DMAx, &dmaConfig);
}

/*!
 * @brief Set adc callback function.
 *
 * @param[in] ADCx: adc module
 *               - ADC0
 * @param[in] func: callback function
 * @return none
 */
void ADC_SetCallback(ADC_Type *ADCx, const DeviceCallback_Type func)
{
    uint8_t instance = 0U;

    instance = ADC_INDEX(ADCx);

    DEVICE_ASSERT(ADC_INSTANCE_MAX > instance);
    s_adcCallback[instance] = func;
}

/*!
 * @brief ADC common interrupt service routine
 *
 * @param[in] ADCx: adc module
 *               - ADC0
 * @return none
 */
static void ADC_CommonISR(ADC_Type *ADCx)
{
    uint8_t instance = 0U;
    uint32_t wpara = 0U;

    instance = ADC_INDEX(ADCx);

    /* store device status */
    wpara = ADCx->STR;

    /* clear device status */
    ADCx->STR = wpara;

    if (NULL != s_adcCallback[instance])
    {
        /* callback */
        s_adcCallback[instance](ADCx, wpara, 0);
    }
}

/*!
 * @brief ADC0 interrupt request handler
 *
 * @param[in] none
 * @return none
 */
void ADC0_IRQHandler(void)
{
    ADC_CommonISR(ADC0);
}

/* =============================================  EOF  ============================================== */
